<script setup lang="ts">
/** 使用示例：
 <ScrollLoadMore
  :loading="loading"
  :finished="finished"
  :error="error"
  :distance="150"
  @load="loadMore"
  @retry="retryLoad"
>
  <div class="content">
    <div v-for="item in items" :key="item.id" class="item">
      {{ item.content }}
    </div>
  </div>
  <!-- 自定义加载提示 -->
  <template #loading>
    <div class="custom-loading">
      <div class="custom-spinner"></div>
      <span>数据加载中，请稍候...</span>
    </div>
  </template>
  <!-- 自定义完成提示 -->
  <template #finished>
    <div class="custom-finished">
      <span>已加载全部数据</span>
    </div>
  </template>
</ScrollLoadMore>

// 模拟数据
interface DataItem {
  id: number
  content: string
}

const items = ref<DataItem[]>([])
const loading = ref(false)
const finished = ref(false)
const error = ref(false)
const page = ref(1)

// 加载更多数据
const loadMore = async () => {
  if (loading.value || finished.value) return
  
  loading.value = true
  error.value = false
  
  try {
    // 模拟API请求
    await new Promise(resolve => setTimeout(resolve, 1000))
    
    // 模拟数据
    const newItems = Array.from({ length: 10 }, (_, i) => ({
      id: (page.value - 1) * 10 + i,
      content: `项目 ${(page.value - 1) * 10 + i} - 随机内容 ${Math.random().toString(36).substring(2, 7)}`
    }))
    
    items.value = [...items.value, ...newItems]
    page.value += 1
    
    // 模拟结束条件
    if (page.value >= 10) {
      finished.value = true
    }
  } catch (err) {
    error.value = true
    console.error('加载数据出错:', err)
  } finally {
    loading.value = false
  }
}

// 重试加载
const retryLoad = () => {
  error.value = false
  loadMore()
}

// 初始加载
onMounted(() => {
  loadMore()
})
 */

interface Props {
  // 是否正在加载
  loading: boolean
  // 是否已加载完成
  finished: boolean
  // 是否加载出错
  error?: boolean
  // 触发加载的距离阈值(像素)
  distance?: number
  // 禁用加载功能
  disabled?: boolean
  // 加载提示位置
  loadingPosition?: 'bottom' | 'center'
  // 完成提示位置
  finishedPosition?: 'bottom' | 'center'
  // 错误提示位置
  errorPosition?: 'bottom' | 'center'
  // 初始加载
  immediate?: boolean
  // 自动检查最大尝试次数
  maxAutoLoad?: number
  // 是否在内容不足时自动加载
  autoLoadWhenShort?: boolean
  // 是否显示状态提示的边距
  hasMargin?: boolean
}

const props = withDefaults(defineProps<Props>(), {
  distance: 100,
  disabled: false,
  loadingPosition: 'bottom',
  finishedPosition: 'bottom',
  errorPosition: 'center',
  immediate: false,
  error: false,
  maxAutoLoad: 2,
  autoLoadWhenShort: true,
  hasMargin: false
})

// 定义事件
const emit = defineEmits<{
  load: [void]
  retry: [void]
}>()

const sentinel = ref<HTMLElement | null>(null)
const containerRef = ref<HTMLElement | null>(null)
const isActive = ref(true)
const autoLoadCount = ref(0)
const isContentShort = ref(false)
const sentinelIsIntersecting = ref(false)

const { stop: stopObserver } = useIntersectionObserver(
  sentinel,
  ([{ isIntersecting }]) => {
    console.log('isIntersecting', isIntersecting)
    sentinelIsIntersecting.value = isIntersecting
    if (isActive.value && isIntersecting && !props.loading && !props.finished && !props.disabled) {
      emitLoad()
    }
  },
  {
    rootMargin: `0px 0px ${props.distance}px 0px`
  }
)

useResizeObserver(containerRef, () => {
  checkContentHeight()
})

// 检查内容高度是否不足
const checkContentHeight = () => {
  if (!containerRef.value || !props.autoLoadWhenShort) return

  // const containerHeight = containerRef.value.clientHeight
  // const contentHeight = containerRef.value.scrollHeight

  // 检查内容高度是否小于容器高度
  // isContentShort.value = contentHeight < containerHeight
  isContentShort.value = sentinelIsIntersecting.value
  // console.log('isContentShort', isContentShort.value, sentinelIsIntersecting.value)
}

const emitLoad = useDebounceFn(() => {
  emit('load')
}, 300)

const retry = () => {
  reset()
  emit('retry')
}

const reset = () => {
  isActive.value = true
  autoLoadCount.value = 0
  isContentShort.value = false
}

const autoLoadCheck = () => {
  if (!props.autoLoadWhenShort) return
  if (props.finished || props.disabled || props.error) return
  if (autoLoadCount.value >= props.maxAutoLoad) return

  nextTick(() => {
    checkContentHeight()

    if (isContentShort.value && !props.loading) {
      autoLoadCount.value += 1
      emitLoad()
    }
  })
}

onMounted(() => {
  if (props.immediate && !props.loading && !props.finished) {
    emitLoad()
  }

  // 初始高度检查
  setTimeout(checkContentHeight, 100)
})

onUnmounted(() => {
  stopObserver()
})

watch(
  () => props.loading,
  (newVal, oldVal) => {
    if (oldVal && !newVal) {
      // 执行自动加载检查
      autoLoadCheck()
    }
  }
)

watch(
  () => props.finished,
  newVal => {
    if (newVal) {
      isActive.value = false
    } else {
      autoLoadCount.value = 0
    }
  }
)

defineExpose({
  reset,
  checkContentHeight
})
</script>

<template>
  <div
    ref="containerRef"
    class="infinite-scroll-container"
  >
    <slot />
    <div
      v-if="loading"
      class="loading-indicator"
      :class="[`position-${loadingPosition}`, { 'has-margin': hasMargin }]"
    >
      <slot name="loading">
        <div class="default-loading">
          <div class="spinner" />
          <span>加载中...</span>
        </div>
      </slot>
    </div>
    <div
      v-if="finished"
      class="finished-indicator"
      :class="[`position-${finishedPosition}`, { 'has-margin': hasMargin }]"
    >
      <slot name="finished">
        <div class="default-finished">没有更多数据了</div>
      </slot>
    </div>
    <div
      v-if="error"
      class="error-indicator"
      :class="[`position-${errorPosition}`, { 'has-margin': hasMargin }]"
    >
      <slot name="error">
        <div class="default-error">
          <span>加载失败</span>
          <button @click="retry">点击重试</button>
        </div>
      </slot>
    </div>

    <!-- 用于检测滚动位置 -->
    <div
      ref="sentinel"
      class="sentinel"
      :style="{ height: `${distance}px` }"
    />
  </div>
</template>

<style scoped>
.infinite-scroll-container {
  position: relative;
  width: 100%;
  min-height: 100px;

  /* max-height: 600px;
  overflow-y: auto; */
}

.sentinel {
  /* position: absolute;
  bottom: 0; */
  width: 100%;
  pointer-events: none;
  border: 1px solid var(--my-color-error-base);
}

.loading-indicator,
.finished-indicator,
.error-indicator {
  padding: 16px 0;
  text-align: center;
  color: var(--my-color-text-1);
}

.loading-indicator.position-center,
.finished-indicator.position-center,
.error-indicator.position-center {
  position: absolute;
  bottom: 50%;
  right: 0;
  left: 0;
  transform: translateY(50%);
}

.loading-indicator.has-margin,
.finished-indicator.has-margin,
.error-indicator.has-margin {
  margin: 16px 0;
}

.default-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}

.spinner {
  width: 20px;
  height: 20px;
  border: 2px solid var(--my-color-mask-1);
  border-top-color: var(--my-color-primary-base);
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

.default-finished {
  padding: 12px;
  color: #999;
}

.default-error {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}

.default-error button {
  padding: 6px 16px;
  color: var(--my-color-text-5);
  background-color: var(--my-color-error-base);
  border: none;
  border-radius: 4px;
  transition: background-color 0.3s;
  cursor: pointer;
}

.default-error button:hover {
  background-color: var(--my-color-error-5);
}
</style>
